/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.listener;

import com.alibaba.fastjson.JSON;
import com.bifrost.constant.OrderStatus;
import com.bifrost.constant.OrderType;
import com.bifrost.constant.RunningMode;
import com.bifrost.entity.Order;
import com.bifrost.entity.OrderTransaction;
import com.bifrost.entity.TransactionSendLog;
import com.bifrost.event.OrderPaidEvent;
import com.bifrost.event.TransactionEvent;
import com.bifrost.exception.ConfirmCanceledOrderException;
import com.bifrost.repository.OrderTransactionRepository;
import com.bifrost.service.OrderService;
import com.bifrost.service.OrderStatusHistoryService;
import com.bifrost.service.PayService;
import com.bifrost.uid.UidGenerator;
import com.bifrost.wechat.entity.WechatAccount;
import com.bifrost.wechat.service.WechatAccountService;
import com.bifrost.wechat.service.WxServiceContext;
import com.bifrost.workflow.Processor;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author joewee
 * @version 1.0.0
 * @Date 2017/12/1 16:59
 */
@Component
public class OrderPaidListener implements ApplicationListener<OrderPaidEvent> {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderSubmittedListener.class);
    @Autowired
    OrderService orderService;
    @Value("${template.Message.submitOrder:#{null}}")
    private String templateId;
    @Value("${view.order.url:#{null}}")
    private String viewOrderUrl;
    @Value("${running.mode:#{null}}")
    private String runningMode;
    //@Autowired
    //WxMpTemplateMsgService wxMpTemplateMsgService;
    @Autowired
    OrderStatusHistoryService statusHistoryService;
    @Autowired
    private WxServiceContext wxServiceContext;
    @Autowired
    WechatAccountService wechatAccountService;
    @Autowired
    PayService swiftPassGateWayService;
    @Autowired
    OrderTransactionRepository orderTransactionRepository;
    @Autowired
    UidGenerator uidGenerator;
    @Qualifier("orderWorkflow")
    @Autowired
    protected Processor orderWorkflow;
    @Autowired
    RedissonClient redisson;
    @Autowired
    ApplicationContext applicationContext;

    @Transactional(rollbackFor = {Exception.class})
    @Override
    @Async
    public void onApplicationEvent(OrderPaidEvent orderPaidEvent) {
        String outTradeNo = orderPaidEvent.getSource().toString();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(String.format("订单%s已经付款成功", outTradeNo));

        }
        Order order = orderService.findOrderByOutTradeNo(outTradeNo);
        RLock lock = redisson.getLock(String.format("%s.%d",Order.LOCK_KEY_PERFIX,order.getOrderId()));
        try{
            //最多等待15秒上锁后10秒自动解锁
           boolean locked = lock.tryLock(15,10, TimeUnit.SECONDS);
           if(locked){
               order = orderService.findOrderByOutTradeNo(outTradeNo);
               if (!OrderStatus.PURCHASED.equalsIgnoreCase(order.getOrderStatus())) {
                   LOGGER.warn(String.format("订单%s已经处于'已确认'状态：%s",outTradeNo,order.getOrderStatus()));
                   return;
               }
               BigInteger orderTotalFee = order.getOrderTotal().multiply(BigDecimal.valueOf(100L)).toBigInteger();
               if (orderTotalFee.compareTo(orderPaidEvent.getTotalFee()) >= 0 || RunningMode.DEVELOPMENT.equalsIgnoreCase(runningMode) ) {
                   String openId = order.getCustomer().getOpenId();
                   if (StringUtils.isNotEmpty(openId)&&StringUtils.isNotBlank(templateId)) {
                       WxMpTemplateData subTitle = new WxMpTemplateData("first", "尊敬的顾客您好，您已成功支付订单,我们将会为您尽快发货", "#173177");
                       WxMpTemplateData orderNumber = new WxMpTemplateData("keyword1", order.getOrderNumber(), "#173177");
                       WxMpTemplateData total = new WxMpTemplateData("keyword2", String.format("%.2f", order.getOrderTotal()), "#173177");
                       //WxMpTemplateData time =new WxMpTemplateData("remark",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(order.getUpdatedTimestamp()),"#173177");
                       WxMpTemplateData remark = new WxMpTemplateData("remark", "欢迎下次光临", "#173177");
                       List<WxMpTemplateData> dataList = new ArrayList<>();
                       dataList.add(subTitle);
                       dataList.add(orderNumber);
                       dataList.add(total);
                       dataList.add(remark);
                       WxMpTemplateMessage message = WxMpTemplateMessage.builder()
                               .templateId(templateId).toUser(openId).url(String.format("%s?orderId=%d", viewOrderUrl, order.getOrderId()))
                               .data(dataList).build();
                       try {
                           Long storeId = order.getMerchantStore().getMerchantId();
                           WechatAccount account = wechatAccountService.getAccount(storeId);
                           if(account!=null){
                               WxMpService service = wxServiceContext.getWxServiceByAppId(account.getAppId());
                               service.getTemplateMsgService().sendTemplateMsg(message);
                           }
                       } catch (WxErrorException e) {
                           LOGGER.error(e.getMessage(), e);
                       }
                   }
                   try{
                       //某种临界情况下此处可能会将已取消的订单设置为已确认状态，目前若确认了已取消的订单则退款处理
                       //团购订单不会简单设置为已确认
                       if(OrderType.CASH_BY_COUPON.equalsIgnoreCase(order.getOrderType())){
                           orderService.finishOrder(order);
                           if(LOGGER.isDebugEnabled()){
                               LOGGER.debug(String.format("优惠券订单%s自动完成成功",outTradeNo));
                               LOGGER.debug(String.format("记录订单状态历史",outTradeNo));
                           }
                           statusHistoryService.writeHistory(order,"付款成功，系统已为您自动完成");
                       }else {
                           orderService.confirmOrder(order);
                           if(LOGGER.isDebugEnabled()){
                               LOGGER.debug(String.format("订单%s自动确认成功",outTradeNo));
                               LOGGER.debug(String.format("记录订单状态历史",outTradeNo));
                           }
                           if(Boolean.TRUE.equals(order.isNeedService())){
                               statusHistoryService.writeHistory(order,"付款成功，系统已为您自动确认");
                           }else{
                               statusHistoryService.writeHistory(order,"付款成功，系统已为您自动确认");
                           }
                       }
                       TransactionEvent evt = new TransactionEvent(order, TransactionSendLog.Type.SALES);
                       applicationContext.publishEvent(evt);
                   }catch (ConfirmCanceledOrderException e){
                       try {
                           LOGGER.error(String.format("订单%s状态异常：%s",outTradeNo,order.getOrderStatus()));
                           Integer fee= order.getOrderTotal().multiply(BigDecimal.valueOf(100)).intValue();
                           if(LOGGER.isDebugEnabled()){
                               LOGGER.debug(String.format("%s执行异常订单退款，金额%d分",outTradeNo,fee));
                           }
                           SortedMap<String, String> map = new TreeMap();
                           map.put("total_fee",fee.toString());
                           map.put("refund_fee",fee.toString());
                           map.put("out_trade_no",outTradeNo);
                           Map<String,String> result = swiftPassGateWayService.refund(map);
                           if("0".equals(result.get("status"))&&"0".equals(result.get("result_code"))){
                               if(LOGGER.isDebugEnabled()){
                                   LOGGER.debug(String.format("记录异常订单%s退款交易记录，金额%d分",outTradeNo,fee));
                               }
                               //记录订单交易事件日志
                               OrderTransaction transaction =new OrderTransaction();
                               transaction.setOrder(order);
                               transaction.setPaymentType(order.getPayment().getDescription());
                               transaction.setTransactionAmount(fee.toString());
                               transaction.setTransactionDate(new Date());
                               transaction.setTransactionType("error_refund");
                               transaction.setUpdatedTimestamp(new Date());
                               transaction.setTransactionDetails(JSON.toJSONString(result));
                               transaction.setBusinessId(result.get("transaction_id"));
                               orderTransactionRepository.save(transaction);
                               if(LOGGER.isDebugEnabled()){
                                   LOGGER.debug(String.format("异常订单%s退款交易记录保存成功",outTradeNo,fee));
                               }
                           }
                       } catch (IOException e1) {
                           LOGGER.error(e1.getMessage(),e1);
                           throw new RuntimeException(e1);
                       }
                   } catch (Exception e1) {
                       LOGGER.error(e1.getMessage(),e1);
                       throw new RuntimeException(e1);
                   }
               }
           }
        } catch (InterruptedException e) {
            LOGGER.error(e.getMessage(),e);
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }
}
